﻿using Microsoft.Office.Server.Search.Administration;
using Microsoft.Office.Server.Search.Administration.Query;
using Microsoft.Office.Server.Search.Query.Rules;
using SPSDeploy.Extensions.Controllers;
using SPSDeploy.Extensions.Extensions;
using SPSDeploy.Extensions.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace SPSDeploy.Extensions.Modules.Extract
{
    /// <summary>
    /// Search proxy extract module
    /// </summary>
    internal class SearchProxyModule : SearchModule
    {

        /// <summary>
        /// Initialization
        /// </summary>
        /// <param name="controller"></param>
        internal SearchProxyModule(BaseController controller)
            : base(controller)
        {
        }

        /// <summary>
        /// Execute module
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        internal override XElement Execute(XElement parent)
        {
            foreach (XElement proxy in parent.Nodes().Where(e => ((XElement)e).Attribute("type").Value == "Search Service Application Proxy").Select(e => e))
            {
                SearchServiceApplicationProxy serviceApplicationProxy = (SearchServiceApplicationProxy)ServiceApplicationProxyExtension.GetServiceApplicationProxy(proxy.Attribute("name").Value, proxy.Attribute("type").Value);
                if (serviceApplicationProxy == null)
                    throw new Exception(string.Format("Service application proxy name '{0}' of type '{1}' is not found.", proxy.Attribute("name").Value, proxy.Attribute("type").Value));
                FederationManager manager = new FederationManager(serviceApplicationProxy);
                XElement result = GetSearchApplicationProxy(serviceApplicationProxy);
                AddResultSources(result, serviceApplicationProxy, manager);
                AddResultTypes(result, ResultTypesExtension.GetOwners(serviceApplicationProxy), manager, new ResultItemTypeManager(serviceApplicationProxy));
                AddQueryRules(result, QueryRulesExtension.GetOwners(serviceApplicationProxy), manager, new QueryRuleManager(serviceApplicationProxy));
                AddManagedProperties(result, serviceApplicationProxy);
                proxy.Add(result);
                controller.OnProgressMessage(string.Format("Search service application proxy '{0}' information extracted.", proxy.Attribute("name").Value));
            }
            return parent;
        }

        #region Search Application Proxy
        /// <summary>
        /// Return search application proxy
        /// </summary>
        /// <param name="proxy"></param>
        /// <returns></returns>
        private XElement GetSearchApplicationProxy(SearchServiceApplicationProxy proxy)
        {
            return new XElement("searchServiceApplicationProxy",
                new XAttribute("mergeWithDefaultPartition", proxy.MergeWithDefaultPartition));
        }
        #endregion

        #region Result Sources
        /// <summary>
        /// Add result sources
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="proxy"></param>
        /// <param name="manager"></param>
        private void AddResultSources(XElement parent, SearchServiceApplicationProxy proxy, FederationManager manager)
        {
            AddResultSources(parent, ResultSourcesExtension.GetOwners(proxy), manager);
            AddDefaultResultSources(parent, ResultSourcesExtension.GetDefaultResultSourceOwners(proxy), manager);
        }
        #endregion

        #region Managed Properties
        /// <summary>
        /// Add managed properties
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="proxy"></param>
        private void AddManagedProperties(XElement parent, SearchServiceApplicationProxy proxy)
        {
            HashSet<int> ids = new HashSet<int>();
            List<ManagedPropertyInfoContainer> properties = new List<ManagedPropertyInfoContainer>();
            foreach (SearchObjectOwner owner in ManagedPropertiesExtension.GetOwners(proxy))
                foreach (ManagedPropertyInfo item in proxy.GetManagedProperties(null, int.MaxValue, owner))
                    if (!ids.Contains(item.Pid))
                    {
                        properties.Add(new ManagedPropertyInfoContainer() { Property = item, Owner = owner });
                        ids.Add(item.Pid);
                    }
            AddManagedProperties(parent, properties);
        }
        #endregion

    }
}